package cc.verywell.pureblock.msg.nfs.idx.extraDownload;

import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;

import cc.verywell.pureblock.func.node.KeyShell;
import cc.verywell.pureblock.log.Pblog;
import cc.verywell.pureblock.msg.nfs.strc.EncodeAble;
import cc.verywell.pureblock.msg.nfs.strc.NfsStructureV1;
import cc.verywell.pureblock.msg.nfs.strc.NfsStructureV1.Sub;
import cc.verywell.pureblock.util.StringUtil;

/**
* 
* Copyright © 2020 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2020年2月2日 下午6:23:53
*/
public class DloLog implements Closeable {

	public ArrayList<Long> posAlias;
	public ArrayList<Long> posSign;
	public ArrayList<Long> posExtra;
	
	public FileChannel channel;
	public DloLog(File f,boolean append) throws IOException {
		this(new FileOutputStream(f,append).getChannel());
	}
	
	public DloLog(FileChannel channel) throws IOException{
		this.channel = channel;
		lastPosition = channel.position();
	}
	
	@Override
	public synchronized void close() throws IOException {
		if(channel!=null) {
		       // try to forces the log to disk
	        try {
	            channel.force(true);
	        }
	        catch (IOException e) { }

	        // close the channel
			channel.close();
			channel = null;
		}
 		
	}

	final static public byte STATE_ALIAS = 0;
	final static public byte STATE_SIGN = 1;
	final static public byte STATE_EXTRA = 2;
	final static public byte STATE_CHECK_POINT = -1;
	final static public byte STATE_INIT_POINT = -2;
	final static public byte STATE_DATA_WAIT = -3;	//数据包等待 [8]pos位置(末端位置) [8]len长度 [2]数据KEY长度   []数据key
	final static public byte STATE_DATA_GET = -4;	//数据包到达 [8]pos位置(末端位置) [8]len长度 [2]校验HASH长度 []校验HASH
	
	public synchronized void logWrite_initpath(KeyShell mainKey,String absolutePath) throws IOException {
		ensureCapacity(mainKey.key.length+8);
		bb.putInt(mainKey.key.length);//+4
		bb.put(mainKey.key);		//+
		bb.putInt((absolutePath==null?0:absolutePath.length()));//+4
		bb.flip();
		while(bb.hasRemaining())channel.write(bb);
		if(absolutePath!=null) {
			ByteBuffer b2 = ByteBuffer.wrap(StringUtil.stringToByte(absolutePath) );
			while(b2.hasRemaining())channel.write(b2);
		}
	}
	public synchronized void logWrite_initsize(long filesize) throws IOException {
		ensureCapacity(9);
		bb.put(STATE_INIT_POINT);//+1
		bb.putLong(filesize);		//+8
		bb.flip();
		while(bb.hasRemaining())channel.write(bb);
	}

	public synchronized void logWrite_get(KeyShell key,byte[] mdHash,DltoolIterator<DltoolPosLen> exportPos) throws IOException {
		//将数据到达内容写入log
		//如果没有mdHash,表明没有数据，更加不会有exportPos
		long size = 2+key.key.length;
		if(mdHash!=null) {
			size += 2+mdHash.length;
			DltoolIterator<DltoolPosLen> p = exportPos;
			while(p!=null) {
				size+=16;
				p = p.prv;
			}
		}
		ensureCapacity(9+size);
		bb.put(STATE_DATA_GET);//+1
		bb.putLong(size);		//+8
		bb.putShort((short)key.key.length); //+2
		bb.put(key.key);
		if(mdHash!=null) {
			bb.putShort((short)mdHash.length); //+2
			bb.put(mdHash);
			DltoolIterator<DltoolPosLen> p = exportPos;
			while(p!=null) {
				bb.putLong(p.t.pos);
				bb.putLong(p.t.len);
				p = p.prv;
			}
		}
		bb.flip();
		while(bb.hasRemaining())channel.write(bb);
	}
	public synchronized void logWrite_wait(KeyShell key,DltoolIterator<DltoolPosLen> exportPos) throws IOException {
		//将数据到达内容写入log
		long size = 2+key.key.length;
		DltoolIterator<DltoolPosLen> p = exportPos;
		while(p!=null) {
			size+=16;
			p = p.prv;
		}
		ensureCapacity(9+size);
		bb.put(STATE_DATA_WAIT);//+1
		bb.putLong(size);		//+8
		bb.putShort((short)key.key.length); //+2
		bb.put(key.key);
		p = exportPos;
		while(p!=null) {
			bb.putLong(p.t.pos);
			bb.putLong(p.t.len);
			p = p.prv;
		}
		bb.flip();
		while(bb.hasRemaining())channel.write(bb);
	}
	static public int SIZE_DATAREAD_DEFAULT = 102500;
	static public int SIZE_DATAREAD_MAX = 100*SIZE_DATAREAD_DEFAULT;

	private ByteBuffer bb = ByteBuffer.allocate(SIZE_DATAREAD_DEFAULT);
	private long lastPosition;
	public synchronized void logWrite_extra(NfsStructureV1 s) throws IOException {
		if(s.alias!=null) {
			long pos = channel.position()+1;
			logWrite_extra(STATE_ALIAS,s.alias);
			if(posAlias==null)posAlias = new ArrayList<>();
			posAlias.add(pos);
		}
		if(s.signs!=null) {
			long pos = channel.position()+1;
			logWrite_extra(STATE_SIGN,s.signs);
			if(posSign==null)posSign = new ArrayList<>();
			posSign.add(pos);
		}
		if(s.links!=null) {
			long pos = channel.position()+1;
			//Pblog.DEBUG("links: +",s.links.length);
			logWrite_extra(STATE_EXTRA,s.links);
			if(posExtra==null)posExtra = new ArrayList<>();
			posExtra.add(pos);
		}
		if(channel.position()!=lastPosition) {
			ensureCapacity(9);
			bb.put(STATE_CHECK_POINT);
			bb.putLong(9+channel.position());
			bb.flip();
			while(bb.hasRemaining())channel.write(bb);
			channel.force(false);
			lastPosition = channel.position();
		}
	}
	private void logWrite_extra(byte state,EncodeAble[] al) throws IOException{
		long size = 0;
		for (EncodeAble u : al) {
			size+=u.encodeSize();
		}
		ensureCapacity(11+size);
		bb.put(state);  //+1
		bb.putLong(size+2);   //+8
		bb.putShort((short)al.length); //+2
		for (EncodeAble u : al) {
			u.encode(bb);
		}
		bb.flip();
		while(bb.hasRemaining())channel.write(bb);
	}
    private void ensureCapacity(long minCapacity) throws IOException {
        if(bb.capacity()>=minCapacity) {
        	bb.clear();
        	return;
         }
        bb = ensureCapacity(bb, minCapacity);
    }
    public static ByteBuffer ensureCapacity(ByteBuffer bb, long minCapacity) throws IOException {
        // overflow-conscious code
    	int oldCapacity = bb.capacity();
        long newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - SIZE_DATAREAD_MAX > 0) {
            newCapacity = SIZE_DATAREAD_MAX;
            if(newCapacity<minCapacity)throw new IOException(minCapacity+" > SIZE_DATAREAD_MAX:"+SIZE_DATAREAD_MAX);
        }
        return ByteBuffer.allocate((int)newCapacity);
    }

	
	
	
}
